﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Pipes.Queries.Parser
{
  public abstract class BaseParser
  {
    protected GOLD.Parser parser = new GOLD.Parser();
    protected string _failMessage;
    protected Stack<object> _stack = new Stack<object>();
    protected ReverseList<object> _top = new ReverseList<object>();

    public string FailMessage
    {
      get
      {
        return _failMessage;
      }
    }

    protected BaseParser(string resourceName)
    {
      //This procedure can be called to load the parse tables. The class can
      //read tables using a BinaryReader.

      var assembly = System.Reflection.Assembly.GetExecutingAssembly();
      
      using (var stream = assembly.GetManifestResourceStream(resourceName))
      using (var reader = new BinaryReader(stream))
      {
        parser.LoadTables(reader);
      }
    }

    public bool Parse(string script)
    {
      using (var r = new System.IO.StringReader(script))
      {
        return this.Parse(r);
      }
    }
    public virtual bool Parse(TextReader reader)
    {
      //This procedure starts the GOLD Parser Engine and handles each of the
      //messages it returns. Each time a reduction is made, you can create new
      //custom object and reassign the .CurrentReduction property. Otherwise, 
      //the system will use the Reduction object that was returned.
      //
      //The resulting tree will be a pure representation of the language 
      //and will be ready to implement.

      _failMessage = "";
      GOLD.ParseMessage response;
      bool done;                      //Controls when we leave the loop
      bool accepted = false;          //Was the parse successful?

      parser.Open(reader);
      parser.TrimReductions = false;  //Please read about this feature before enabling  

      done = false;
      while (!done)
      {
        response = parser.Parse();

        switch (response)
        {
          case GOLD.ParseMessage.LexicalError:
            //Cannot recognize token
            _failMessage = "Lexical Error:\n" +
                          "Position: " + parser.CurrentPosition().Line + ", " + parser.CurrentPosition().Column + "\n" +
                          "Read: " + parser.CurrentToken().Data;
            done = true;
            break;

          case GOLD.ParseMessage.SyntaxError:
            //Expecting a different token
            _failMessage = "Syntax Error:\n" +
                          "Position: " + parser.CurrentPosition().Line + ", " + parser.CurrentPosition().Column + "\n" +
                          "Read: " + parser.CurrentToken().Data + "\n" +
                          "Expecting: " + parser.ExpectedSymbols().Text();
            done = true;
            break;

          case GOLD.ParseMessage.Reduction:
            //Create a customized object to store the reduction
            ActOnReduction(parser.CurrentReduction as GOLD.Reduction);
            break;

          case GOLD.ParseMessage.Accept:
            //Accepted!
            //program = parser.CurrentReduction   //The root node!                 
            done = true;
            accepted = true;
            break;

          case GOLD.ParseMessage.TokenRead:
            //You don't have to do anything here.
            break;

          case GOLD.ParseMessage.InternalError:
            //INTERNAL ERROR! Something is horribly wrong.
            done = true;
            break;

          case GOLD.ParseMessage.NotLoadedError:
            //This error occurs if the CGT was not loaded.                   
            _failMessage = "Tables not loaded";
            done = true;
            break;

          case GOLD.ParseMessage.GroupError:
            //GROUP ERROR! Unexpected end of file
            _failMessage = "Runaway group";
            done = true;
            break;
        }
      } //while

      return accepted;
    }

    protected virtual void ActOnReduction(GOLD.Reduction reduc)
    {
      _top.Clear();
    }
    protected void TakeFromStack(int count)
    {
      for (var i = 0; i < count; i++)
      {
        _top.Push(_stack.Pop());
      }
    }
    protected void InsertStack(int depth, object value)
    {
      TakeFromStack(depth);
      _stack.Push(value);
      while (_top.Count > 0)
      {
        _stack.Push(_top.Pop());
      }
    }
  }
}
